use gset::Getset;
use sandbox::file_response_list::SameCsv;
use sandbox::get_file_list_client::GetFileListClient;
use sandbox::invoke_client::InvokeClient;
use sandbox::{FileRequest, FileResponse, FileResponseList, PythonRequest};
use tonic::Request;
pub mod sandbox {
    tonic::include_proto!("sandbox");
}
#[derive(Debug, Default, Getset)]
pub struct PythonRequestBuilder {
    #[getset(set_own, name = "username", vis = "pub")]
    username: String,
    #[getset(set_own, name = "filename", vis = "pub")]
    filename: String,
}
impl PythonRequestBuilder {
    #[inline]
    pub fn build(self) -> PythonRequest {
        PythonRequest {
            filename: self.filename,
            username: self.username,
        }
    }
}
impl PythonRequest {
    #[inline]
    pub fn builder() -> PythonRequestBuilder {
        PythonRequestBuilder::default()
    }
}
#[derive(Debug, Default, serde::Serialize)]
pub struct FileInfo {
    filename: String,
    file_extensions: String,
    filesize: String,
    modified_time: String,
}
#[derive(Debug, Default, serde::Serialize)]
pub struct List {
    csv_name: String,
    element: Vec<FileInfo>,
}
impl FileResponseList {
    pub fn take(self) -> Vec<Vec<FileResponse>> {
        let FileResponseList { diff_files } = self;
        let res = diff_files
            .into_iter()
            .map(|item| {
                let SameCsv { files } = item;

                files
            })
            .collect();
        res
    }
}
impl From<FileResponse> for FileInfo {
    #[inline]
    fn from(value: FileResponse) -> Self {
        let FileResponse {
            filename,
            file_extensions,
            filesize,
            modified_time,
            ..
        } = value;
        Self {
            filename,
            file_extensions,
            filesize,
            modified_time,
        }
    }
}
impl From<Vec<FileResponse>> for List {
    #[inline]
    fn from(value: Vec<FileResponse>) -> Self {
        let csv_name = value.get(0).unwrap().csv_name.to_owned();
        let element = value
            .into_iter()
            .map(|item| item.convert::<FileInfo>())
            .collect();
        Self { csv_name, element }
    }
}
impl FileResponse {
    #[inline]
    pub fn convert<T: From<FileResponse>>(self) -> T {
        T::from(self)
    }
}
pub async fn predict_by_seir(filename: String, username: String) -> anyhow::Result<()> {
    let mut client = InvokeClient::connect("http://[::1]:50050").await?;
    let request = Request::new(
        PythonRequest::builder()
            .filename(filename)
            .username(username)
            .build(),
    );
    let response = client.seir_predict(request).await?;
    log::info!("{:#?}", response);
    Ok(())
}
pub async fn predict_by_sir(_username: String, _filename: String) -> anyhow::Result<()> {
    Ok(())
}

pub async fn get_files(username: String) -> anyhow::Result<FileResponseList> {
    let mut client = GetFileListClient::connect("http://[::1]:50050").await?;
    let request = Request::new(FileRequest { username });
    let response = client.get_file_list(request).await?.into_inner();
    Ok(response)
}

#[cfg(test)]
mod tests {
    use crate::predict_by_seir;

    #[test]
    fn client_invoke() {
        let rt = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .worker_threads(8)
            .build()
            .unwrap();
        let res = rt.block_on(async {
            predict_by_seir("02_SZ_DailyCases.csv".into(), "root".into())
                .await
                .map_err(|e| {
                    println!("{}", e.to_string());
                })
        });
        assert_eq!(res, Ok(()));
    }
}
